﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Quicklight.Web {

   /// <summary>
   /// The CalculatedProperty class calculates its value from a Func delegate that is passed to it in its contructor. 
   /// If any of the IObservable dependancies passed to the constructor fires its ValueChanged event, the CalculatedProperty will fire its ValueChanged event.
   /// The list of IObservable dependancies should therefore contain all the IObservable's that the Func delegate is dependant upon
   /// </summary>
   /// <typeparam name="T">The return type of the Value property and the Func delegate of the CalculatedProperty</typeparam>
   public class CalculatedProperty<T> : IObservable {
      private Func<T> func;
      private Validation validation;

      /// <summary>
      /// Constructs a CalculatedProperty instance that will use the specified Func delegate to calculate its Value property and 
      /// fire its ValueChanged event if any of the dependancies in the list of IObservables changes its value
      /// </summary>
      /// <param name="func">The delegate to use in calculating the Value property</param>
      /// <param name="dependancies">A list of IObservables that together determine the return value of the Func delegate</param>
      public CalculatedProperty(Func<T> func, IList<IObservable> dependancies) {
         this.func = func;
         this.validation = new Validation(this);

         foreach (IObservable observable in dependancies)
            observable.ValueChanged += new EventHandler(observable_ValueChanged);
      }

      /// <summary>
      /// Event that fires if any of the dependancies of the CalculatedProperty change value, thereby changing the return of the Value property
      /// </summary>
      public event EventHandler ValueChanged;

      /// <summary>
      /// Explicit interface implementation of the Value property of the IObservable interface. 
      /// Returns the Value property as an object
      /// </summary>
      object IObservable.Value {
         get {
            return this.Value;
         }
      }

      /// <summary>
      /// Returns the result of invoking the Func delegate supplied to the constructor of this instance
      /// </summary>
      public T Value {
         get { 
            return func(); 
         }
      }

      /// <summary>
      /// The Validation instance for this CalculatedProperty. 
      /// If the ValueChanged event of this instance is fired it validates the result of the Value property
      /// </summary>
      public Validation Validation {
         get {
            return this.validation;
         }
         set {
            this.validation = value;
            this.validation.Observable = this;
         }
      }

      /// <summary>
      /// Event handler for ValueChanged events on IObservables supplied in list of dependancies to contructor. 
      /// Calls the OnValueChanged private method to fire the ValueChanged event of this instance
      /// </summary>
      /// <param name="sender">IObservable that fired the event</param>
      /// <param name="e">Equal to EventArgs.Empty</param>
      private void observable_ValueChanged(object sender, EventArgs e) {
         OnValueChanged();
      }

      /// <summary>
      /// Fires the ValueChanged event if there are subscribers
      /// </summary>
      private void OnValueChanged() {
         if (ValueChanged != null)
            ValueChanged(this, EventArgs.Empty);
      }

   }
}
